package com.ops.uitl.tapestry;

import java.io.Serializable;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.transaction.UserTransaction;
import net.sf.json.JSONArray;
import org.apache.commons.fileupload.FileItem;
import org.apache.log4j.Logger;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.request.IUploadFile;
import org.apache.tapestry.request.RequestContext;
import com.ops.uitl.common.Common;
import com.ops.uitl.data.DataMap;
import com.ops.uitl.data.DatasetList;
import com.ops.uitl.data.IData;
import com.ops.uitl.data.IDataset;
import com.ops.uitl.jdbc.ConnectionManager;
import com.ops.uitl.jdbc.Pagination;
/*
import com.ops.uitl.flow.pageflow.IPageFlow;
import com.ops.uitl.flow.pageflow.PageFlow;
import com.ops.uitl.file.FileUpload;
import com.ops.uitl.file.FileUtil;
*/
public class BaseData implements Serializable {

	protected Logger log = Logger.getLogger(this.getClass());
	protected Common common = Common.getInstance();

	private static List blackList = new ArrayList();

	protected BaseContext ctx;
	private HttpServletRequest request;
	private IRequestCycle cycle;
	private RequestContext context;
	private String defaultDBConnName;
	private String routeEparchyCode;

	private Map connItems = new DataMap();
	private IData currentData = new DataMap();
	private IData receiveData = new DataMap();
	private IData transferData = new DataMap();
	private IData fileItems = new DataMap();
	private Map paginations = new HashMap();
	private IDataset ajaxset = new DatasetList();

	private boolean multipart = false;
	private boolean autoCommit = true;

	private String transactionMode = ConnectionManager.TRANSACTION_MODE_JDBC;
	private UserTransaction utx;

	static {
		setBlackList();
	}

	/**
	 * set black list
	 */
	protected static void setBlackList() {
		blackList.add("listener");
		blackList.add("service");
		blackList.add("sp");
		blackList.add("Form0");
	}

	/**
	 * construct function
	 * @throws Exception
	 */
	public BaseData() throws Exception {
		super();
	}

	/**
	 * construct function
	 * @param cycle
	 * @throws Exception
	 */
	public BaseData(IRequestCycle cycle) throws Exception {
		this.cycle = cycle;
		context = cycle.getRequestContext();
		initialize();
	}

	/**
	 * construct function
	 * @param request
	 * @throws Exception
	 */
	public BaseData(HttpServletRequest request) throws Exception {
		this.request = request;
		initialize();
	}

	/**
	 * initialize data
	 * @throws Exception
	 */
	protected void initialize() throws Exception {
		putDataByRequest(getRequest());

		if (cycle != null) {
			IData receiveData = getReceiveData(cycle);
			if (receiveData != null) {
				currentData.putAll(receiveData);
				this.receiveData.putAll(receiveData);
			}
		}
	}

	/**
	 * get receive data
	 * @param cycle
	 * @return IData
	 * @throws Exception
	 */
	protected IData getReceiveData(IRequestCycle cycle) throws Exception {
		Object[] params = cycle.getServiceParameters();
		if (params != null && params.length == 1 && params[0] instanceof IData) {
			return (IData) params[0];
		}
		return null;
	}

	/**
	 * transfer data
	 * @param data
	 * @throws Exception
	 */
	protected void transferData() throws Exception {
		cycle.setServiceParameters(new Object[] { getTransferData() });
	}

	/**
	 * put data by request
	 * @param request
	 * @throws Exception
	 */
	protected void putDataByRequest(HttpServletRequest request) throws Exception {
		Enumeration fields = request.getParameterNames();
		while (fields.hasMoreElements()) {
			String field = (String) fields.nextElement();
			if (blackList.contains(field)) continue;

			String[] values = request.getParameterValues(field);
			if (values.length > 1) {
				currentData.put(field, values);
			} else {

				currentData.put(field, values[0]);
			}
		}
	}

	/**
	 * put data by stream
	 * @param cycle
	 * @throws Exception
	 */
	protected void putDataByStream(IRequestCycle cycle) throws Exception {
		String formParams = cycle.getRequestContext().getParameter("_FormParams");
		if (formParams != null && !"".equals(formParams)) {
			List names = Arrays.asList(formParams.split(","));
			for (int i=0; i<names.size(); i++) {
				String name = (String) names.get(i);
				if (blackList.contains(name)) continue;
				String[] values = cycle.getRequestContext().getParameters(name);
				if (values == null) continue;
				if (values.length > 1) {
					currentData.put(name, values);
				} else {
					currentData.put(name, values[0]);
				}
			}
		}
	}


	/**
	 * get auto commit
	 * @return boolean
	 * @throws Exception
	 */
	public boolean getAutoCommit() throws Exception {
		return autoCommit;
	}

	/**
	 * set auto commit
	 * @param autoCommit
	 * @throws Exception
	 */
	public void setAutoCommit(boolean autoCommit) throws Exception {
		this.autoCommit = autoCommit;
	}

	/**
	 * is transaction
	 * @return String
	 * @throws Exception
	 */
	public String getTransactionMode() throws Exception {
		return transactionMode;
	}

	/**
	 * set transaction
	 * @param transactionMode
	 * @throws Exception
	 */
	public void setTransactionMode(String transactionMode) throws Exception {
		this.transactionMode = transactionMode;
	}

	/**
	 * begin user transaction
	 * @param transactionMode
	 * @throws Exception
	 */
	public void beginUserTransaction() throws Exception {
		if (utx != null) log.error("user transaction already exist!");
		if (log.isDebugEnabled()) log.debug("begin xa transaction...");
		setTransactionMode(ConnectionManager.TRANSACTION_MODE_XA);
		utx = ConnectionManager.getInstance().getUserTransaction();
		utx.begin();
	}

	/**
	 * end user transaction
	 * @param transactionMode
	 * @throws Exception
	 */
	public void endUserTransaction() throws Exception {
		if (utx == null) log.error("user transaction not exist!");
		if (log.isDebugEnabled()) log.debug("end xa transaction...");
		utx.commit();
	}

	/**
	 * get user transaction
	 * @return UserTransaction
	 * @throws Exception
	 */
	protected UserTransaction getUserTransaction() throws Exception {
		return utx;
	}

	/**
	 * get default db conn name
	 * @return String
	 * @throws Exception
	 */
	public String getDefaultDBConnName() throws Exception {
		return defaultDBConnName != null ? defaultDBConnName : ConnectionManager.getInstance().getDefaultName(routeEparchyCode == null ? getContext().getTradeEpachyId() : routeEparchyCode);
	}

	/**
	 * set default db conn name
	 * @param defaultDBConnName
	 * @throws Exception
	 */
	public void setDefaultDBConnName(String defaultDBConnName) throws Exception {
		this.defaultDBConnName = defaultDBConnName;
	}

	/**
	 * set default login eparchy
	 * @param login_eparchy_id
	 * @throws Exception
	 */
	public void setDefaultLoginEparchy(String login_eparchy_id) throws Exception {
		this.defaultDBConnName = ConnectionManager.getInstance().getDefaultName(login_eparchy_id);
	}

	/**
	 * get default db conn name
	 * @return String
	 * @throws Exception
	 */
	public String getRouteEparchy() throws Exception {
		return routeEparchyCode;
	}

	/**
	 * set route eparchy
	 * @param routeEparchyCode
	 * @throws Exception
	 */
	public void setRouteEparchy(String routeEparchyCode) throws Exception {
		this.routeEparchyCode = routeEparchyCode;
	}

	/**
	 * get db connection
	 * @return Connection
	 * @throws Exception
	 */
	public Connection getDBConn() throws Exception {
		return getDBConn(getDefaultDBConnName());
	}

	/**
	 * get db connection
	 * @param name
	 * @return Connection
	 * @throws Exception
	 */
	public Connection getDBConn(String name) throws Exception {
		if (name == null) return getDBConn();
		Connection conn = (Connection) connItems.get(name);
		if (conn != null) return conn;

		ConnectionManager manager = ConnectionManager.getInstance();
		conn = manager.getConnection(name, transactionMode);
		conn.setAutoCommit(false);
		connItems.put(name, conn);

		return conn;
	}

	/**
	 * set db connection
	 * @param conn
	 * @throws Exception
	 */
	public void setDBConn(Connection conn) throws Exception {
		setDBConn(getDefaultDBConnName(), conn);
	}

	/**
	 * set db connection
	 * @param name
	 * @param conn
	 * @throws Exception
	 */
	public void setDBConn(String name, Connection conn) throws Exception {
		connItems.put(name, conn);
	}

	/**
	 * close db connection
	 * @throws Exception
	 */
	public void closeDBConn() throws Exception {
		closeDBConn(getDefaultDBConnName());
	}

	/**
	 * close db connection
	 * param name
	 * @throws Exception
	 */
	public void closeDBConn(String name) throws Exception {
		String transactionMode = getTransactionMode();
		Connection conn = (Connection) connItems.get(name);
		if (conn != null && !conn.isClosed()) {
			ConnectionManager manager = ConnectionManager.getInstance();
			boolean alreadyCommit = conn.getAutoCommit();
			try {
				if (ConnectionManager.TRANSACTION_MODE_JDBC.equals(transactionMode) && !alreadyCommit) {
					if (getAutoCommit()) conn.commit();
				}
			} catch (Exception e) {
				if (ConnectionManager.TRANSACTION_MODE_JDBC.equals(transactionMode) && !alreadyCommit) {
					log.warn("commit failed, rollback..." + name);
					//conn.rollback();
					//throw e;
				}
			} finally {
				if (log.isDebugEnabled()) log.debug("close " + transactionMode + " db connection..." + name);
				manager.closeConnection(conn);
			}
		}
	}

	/**
	 * exist db connection
	 * @return boolean
	 * @throws Exception
	 */
	public boolean existDBConn() throws Exception {
		return existDBConn(getDefaultDBConnName());
	}

	/**
	 * exist db connection
	 * @param name
	 * @return boolean
	 * @throws Exception
	 */
	public boolean existDBConn(String name) throws Exception {
		Connection conn = (Connection) connItems.get(name);
		return conn != null && !conn.isClosed();
	}

	/**
	 * rollback all existing db connections
	 * @throws Exception
	 */
	public void rollbackConnections() throws Exception {
		if (ConnectionManager.TRANSACTION_MODE_JDBC.equals(transactionMode)) {
			Iterator it = getDBConns().entrySet().iterator();
			while (it.hasNext()) {
				Entry entry = (Entry) it.next();
				Connection conn = (Connection) entry.getValue();
				log.error("execute failed, rollback..." + entry.getKey());
				conn.rollback();
			}
		} else if (ConnectionManager.TRANSACTION_MODE_XA.equals(transactionMode)) {
			log.error("execute failed, rollback " + transactionMode + " ...");
			utx.rollback();
		}
	}

	/**
	 * cleanup all existing db connections
	 * @throws Exception
	 */
	public void cleanupConnections() throws Exception {
		Iterator it = connItems.keySet().iterator();
		while (it.hasNext()) {
			String name = (String) it.next();
			closeDBConn(name);
		}
		connItems = new DataMap();
	}

	/**
	 * commit db conn by default name
	 * @throws Exception
	 */
	public void commitDBConn() throws Exception {
		commitDBConn(getDefaultDBConnName());
	}


	/**
	 * commit db conn by name
	 * @param name
	 * @throws Exception
	 */
	public void commitDBConn(String name) throws Exception {
		String transactionMode = getTransactionMode();
		Connection conn = (Connection) connItems.get(name);
		if (conn != null && !conn.isClosed()) {
			boolean alreadyCommit = conn.getAutoCommit();
			try {
				if (ConnectionManager.TRANSACTION_MODE_JDBC.equals(transactionMode) && !alreadyCommit) {
					if (getAutoCommit()) conn.commit();
				}
			} catch (Exception e) {
				if (ConnectionManager.TRANSACTION_MODE_JDBC.equals(transactionMode) && !alreadyCommit) {
					log.warn("commit failed, rollback..." + name);
				}
			} finally {
				if (log.isDebugEnabled()) log.debug("commit " + transactionMode + " db connection..." + name);
			}
		}
	}


	/**
	 * commit all db conns
	 * @throws Exception
	 */
	public void commitDBConns() throws Exception {
		Iterator it = connItems.keySet().iterator();
		while (it.hasNext()) {
			String name = (String) it.next();
			commitDBConn(name);
		}
	}

	/**
	 * set db conns
	 * @param connItems
	 * @throws Exception
	 */
	public void setDBConns(Map connItems) throws Exception {
		this.connItems = connItems;
	}

	/**
	 * get db conns
	 * @return Map
	 * @throws Exception
	 */
	public Map getDBConns() throws Exception {
		return connItems;
	}

	/**
	 * get request cycle
	 * @return IRequestCycle
	 */
	public IRequestCycle getRequestCycle() {
		return cycle;
	}

	/**
	 * get request context
	 * @return RequestContext
	 */
	public RequestContext getRequestContext() {
		return context;
	}

	/**
	 * get response
	 * @return HttpServletResponse
	 */
	public HttpServletResponse getResponse() {
		return cycle == null ? null : context.getResponse();
	}

	/**
	 * get request
	 * @return HttpServletRequest
	 */
	public HttpServletRequest getRequest() {
		return cycle == null ? request : context.getRequest();
	}

	/**
	 * get session
	 * @return HttpSession
	 */
	public HttpSession getSession() {
		return getRequest().getSession();
	}

	/**
	 * get context
	 * @return BaseContext
	 */
	public BaseContext getContext() {
		return ctx;
	}

	/**
	 * set context
	 * @param ctx
	 */
	public void setContext(BaseContext ctx) {
		this.ctx = ctx;
	}

	/**
	 * get parameter
	 * @param name
	 * @return String
	 * @throws Exception
	 */
	public String getParameter(String name) throws Exception {
		return (String) currentData.get(name);
	}

	/**
	 * get parameter
	 * @param name
	 * @param def
	 * @return String
	 * @throws Exception
	 */
	public String getParameter(String name, String def) throws Exception {
		String value = getParameter(name);
		return value == null ? def : value;
	}

	/**
	 * set parameter
	 * @param name
	 * @param value
	 * @throws Exception
	 */
	public void setParameter(String name, String value) throws Exception {
		if (value != null) currentData.put(name, value);
	}

	/**
	 * get parameters
	 * @param name
	 * @return String[]
	 * @throws Exception
	 */
	public String[] getParameters(String name) throws Exception {
		Object value = currentData.get(name);
		return common.getValues(value);
	}

	/**
	 * set parameters
	 * @param name
	 * @param values
	 * @throws Exception
	 */
	public void setParameters(String name, String[] values) throws Exception {
		currentData.put(name, values);
	}

	/**
	 * get upload file
	 * @param name
	 * @return IUploadFile
	 * @throws Exception
	 */
	public IUploadFile getUploadFile(String name) throws Exception {
		return context.getUploadFile(name);
	}


	/**
	 * get file item
	 * @param field
	 * @return FileItem
	 * @throws Exception
	 */
	public FileItem getFileItem(String field) throws Exception {
		return (FileItem) fileItems.get(field);
	}

	/**
	 * get file items
	 * @return IData
	 * @throws Exception
	 */
	public IData getFileItems() throws Exception {
		return fileItems;
	}

	/**
	 * set transfer
	 * @param name
	 * @throws Exception
	 */
	public void setTransfer(String name) throws Exception {
		Object value = currentData.get(name);
		if (value != null) {
			transferData.put(name, value);
			transferData();
		}
	}

	/**
	 * remove transfer
	 * @param name
	 * @throws Exception
	 */
	public void removeTransfer(String name) throws Exception {
		transferData.remove(name);
		transferData();
	}

	/**
	 * set transfer
	 * @param name
	 * @param value
	 * @throws Exception
	 */
	public void setTransfer(String name, String value) throws Exception {
		currentData.put(name, value);
		transferData.put(name, value);
		transferData();
	}

	/**
	 * set transfers
	 * @param name
	 * @param values
	 * @throws Exception
	 */
	public void setTransfers(String name, String[] values) throws Exception {
		currentData.put(name, values);
		transferData.put(name, values);
		transferData();
	}

	/**
	 * set transfer
	 * @param group
	 * @throws Exception
	 */
	public void setTransferData(String group) throws Exception {
		setTransferData(getData(group));
	}

	/**
	 * set transfer data
	 * @param data
	 * @throws Exception
	 */
	public void setTransferData(IData data) throws Exception {
		currentData.putAll(data);
		transferData.putAll(data);
		transferData();
	}

	/**
	 * set transfer
	 * @param group
	 * @param data
	 * @throws Exception
	 */
	public void setTransferData(String group, IData data) throws Exception {
		String[] names = data.getNames();
		for (int i=0; i<names.length; i++) {
			String key = group + "_" + names[i];
			Object value = data.get(names[i]);
			currentData.put(key, value);
			transferData.put(key, value);
		}
		transferData();
	}

	/**
	 * get transfer data
	 * @return IData
	 * @throws Exception
	 */
	public IData getTransferData() throws Exception {
		return transferData;
	}

	/**
	 * get receive data
	 * @return IData
	 * @throws Exception
	 */
	public IData getReceiveData() throws Exception {
		return receiveData;
	}

	/**
	 * get data
	 * @param group
	 * @return IData
	 * @throws Exception
	 */
	public IData getData(String group) throws Exception {
		return getData(group, false);
	}

	/**
	 * get data
	 * @param group
	 * @param istrim
	 * @return IData
	 * @throws Exception
	 */
	public IData getData(String group, boolean istrim) throws Exception {
		IData element = new DataMap();

		String[] names = currentData.getNames();
		for (int i=0; i<names.length; i++) {
			if (names[i].startsWith(group + "_")) {
				element.put(istrim ? names[i].substring((group + "_").length()) : names[i], currentData.get(names[i]));
			}
		}

		return element;
	}

	/**
	 * get data
	 * @return IData
	 * @throws Exception
	 */
	public IData getData() throws Exception {
		return currentData;
	}

	/**
	 * set data
	 * @param data
	 * @throws Exception
	 */
	public void setData(IData data) throws Exception {
		currentData = data;
	}

	/**
	 * set data
	 * @param data
	 * @param group
	 * @param istrim
	 * @throws Exception
	 */
	public void setData(IData data, String group) throws Exception {
		IData element = new DataMap();

		String[] names = data.getNames();
		for (int i=0; i<names.length; i++) {
			element.put(group+"_"+names[i], data.get(names[i]));
		}

		currentData = element;
	}

	/**
	 * get remote addr
	 * @return String
	 * @throws Exception
	 */
	public String getRemoteAddr() throws Exception {
		HttpServletRequest req = getRequest();
		String ip = req.getParameter("CLIENT_IP_ADDR");
		if (ip == null || ip.length() == 0) ip = req.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) ip = req.getHeader("Proxy-Client-IP");
		if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) ip = req.getHeader("WL-Proxy-Client-IP");
		if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) ip = req.getRemoteAddr();
		return ip;
	}

	/**
	 * get pagination
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination() throws Exception {
		return getPagination("default_pagin");
	}

	/**
	 * get pagination
	 * @param needCount
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination(boolean needCount) throws Exception {
		return getPagination("default_pagin", needCount);
	}

	/**
	 * get pagination
	 * @param pagin_name
	 * @param page_size
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination(int page_size) throws Exception {
		return getPagination("default_pagin", page_size);
	}

	/**
	 * get pagination
	 * @param pagin_name
	 * @param page_size
	 * @param needCount
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination(int page_size, boolean needCount) throws Exception {
		return getPagination("default_pagin", page_size, needCount);
	}

	/**
	 * get pagination
	 * @param pagin_name
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination(String pagin_name) throws Exception {
		return getPagination(pagin_name, getDefaultPageSize());
	}

	/**
	 * get pagination
	 * @param pagin_name
	 * @param needCount
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination(String pagin_name, boolean needCount) throws Exception {
		return getPagination(pagin_name, getDefaultPageSize(), needCount);
	}

	/**
	 * get pagination
	 * @param pagin_name
	 * @param size
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination(String pagin_name, int size) throws Exception {
		return getPagination(pagin_name, size, true);
	}

	/**
	 * get pagination
	 * @param pagin_name
	 * @param size
	 * @param needCount
	 * @return Pagination
	 * @throws Exception
	 */
	public Pagination getPagination(String pagin_name, int size, boolean needCount) throws Exception {
		String[] sizes = getParameters(pagin_name + "_pagesize");
		int pageSize = sizes.length == 0 ? size : Integer.parseInt(sizes[0]);

		paginations.put(pagin_name, String.valueOf(pageSize));

		String currPage = getParameter(pagin_name, "1");
		String paginCount = getParameter(pagin_name + "_count");
		String paginNeedCount = getParameter(pagin_name + "_needCount");

		Pagination pagination = new Pagination();
		pagination.setRange((Integer.parseInt(currPage) - 1) * pageSize, pageSize);
		pagination.setCurrPage(Integer.parseInt(currPage));
		if (paginNeedCount != null && "true".equals(paginNeedCount)) return pagination;

		if (!needCount || paginCount != null) {
			pagination.setNeedCount(false);
			pagination.setCount(paginCount != null ? Integer.parseInt(paginCount) : Pagination.MAX_RECODE_SIZE);
		}

		return pagination;
	}

	/**
	 * get page size
	 * @return int
	 * @throws Exception
	 */
	public int getPageSize() throws Exception {
		return getPageSize("default_pagin");
	}

	/**
	 * get page size
	 * @param pagin_name
	 * @return int
	 * @throws Exception
	 */
	public int getPageSize(String pagin_name) throws Exception {
		String pageSize = (String) paginations.get(pagin_name);
		return pageSize == null ? 0 : Integer.parseInt(pageSize);
	}

	/**
	 * get default page size
	 * @return int
	 * @throws Exception
	 */
	public int getDefaultPageSize() throws Exception {
		return Integer.parseInt(common.getProperty("globar/navbar/pagesize"));
	}

	/**
	 * get max page size
	 * @return int
	 * @throws Exception
	 */
	public int getMaxPageSize() throws Exception {
		return Integer.parseInt(common.getProperty("globar/navbar/maxpagesize"));
	}

	/**
	 * set ajax输出数据
	 * @deprecated deprecated,use the setAjaxDataset,setAjaxData,setAjaxString method.
	 * add by zhujm 2008-2-15
	 */
	public void setAjaxData(IDataset datas) throws Exception{
		JSONArray tmp = JSONArray.fromObject(datas);
		cycle.setAttribute("AJAXDATA",tmp.toString());
	}

	/**
	 * set ajax string
	 * @param name
	 * @param value
	 * @throws Exception
	 */
	public void setAjaxString(String name, String value) throws Exception{
		IData data = new DataMap();
		data.put(name, value);
		setAjaxData(data);
	}

	/**
	 * set ajax data
	 * @param data
	 * @throws Exception
	 */
	public void setAjaxData(IData data) throws Exception{
		ajaxset.add(data);
	}

	/**
	 * set ajax dataset
	 * @param dataset
	 * @throws Exception
	 */
	public void setAjaxDataset(IDataset dataset) throws Exception{
		ajaxset.addAll(dataset);
	}

	/**
	 * get ajax dataset
	 * @return IDataset
	 * @throws Exception
	 */
	public IDataset getAjaxDataset() throws Exception{
		return ajaxset;
	}

}